#ifndef _MODESTO_COMMAND_H_
#define _MODESTO_COMMAND_H_

#include <vector>
#include <map>
#include "mutex.h"
#include <iostream>

struct CmdOriginator
{
	bool isUser;
	std::map<const char *, std::string> parts;
};

struct RawCmd
{
	int numeric;
	bool isValid;
	std::string raw, prefix, command;
	std::vector<std::string> params;
	CmdOriginator originator;
	int server;

	RawCmd(int server, const std::string &cmd) : server(server)
	{
		std::string part;
		size_t tokpos, lastpos, tmp1, tmp2;
		
		this->raw = cmd;
		this->isValid = true;

		lastpos = 0;

		// If the first whitespace found is the last, then the command is invalid.
		if ((tokpos = cmd.find(" ")) == std::string::npos)
		{
			this->isValid = false;

			return;
		}

		// The first part is the prefix, which contains the very first part of the command, up to the whitespace
		part = cmd.substr(lastpos, tokpos);

		// Skip the white space, and set the current position for the next phrase.
		lastpos = tokpos + 1;
		this->prefix = part;

		// if the prefix does not start with a colon (seperator)
		// then we know the prefix is the command name (PRIVMSG, NOTICE, etc)
		if (part.compare(0, 1, ":") != 0)
		{
			this->command = part;

			// If there is another white space after this one
			if (cmd.find(" ", lastpos) != std::string::npos)
			{
				for (tokpos = cmd.find(" ", lastpos); tokpos != cmd.length()-1; tokpos = cmd.find(" ", lastpos))
				{
					if (cmd.compare(lastpos, 1, ":") == 0)
					{
						lastpos++;
						tokpos = cmd.find(" ", lastpos);
						break;
					}

					if (tokpos == std::string::npos)
					{
						this->params.push_back(cmd.substr(lastpos));
						break;
					}

					this->params.push_back(cmd.substr(lastpos, tokpos - lastpos));
					lastpos = tokpos + 1;
				}
			}

			// We are at the end of the line (no more white spaces)
			else
			{
				if (cmd.compare(lastpos, 1, ":") == 0)
				{
					this->params.push_back(cmd.substr(lastpos + 1));
				}
				else
				{
					this->params.push_back(cmd.substr(lastpos));
				}
			}

			return;
		}


		part.erase(0, 1);

		// If the command starts with a bang (!) then the message is from a user.
		if ((tmp1 = part.find("!")) != std::string::npos)
		{
			this->originator.isUser = true;
			this->originator.parts.insert(std::pair<const char *, std::string>("nick", part.substr(0, tmp1)));

			if ((tmp2 = part.find("@")) != std::string::npos)
			{
				this->originator.parts.insert(std::pair<const char *, std::string>("user", part.substr(tmp1 + 1, tmp2 - tmp1 - 1)));
				this->originator.parts.insert(std::pair<const char *, std::string>("host", part.substr(tmp2 + 1)));
			}
			else
			{
				this->originator.parts.insert(std::pair<const char *, std::string>("user", part.substr(tmp1 + 1)));
			}
		}

		// If there is no bang (!), then the message is from the server
		else
		{
			this->originator.isUser = false;
			this->originator.parts.insert(std::pair<const char *, std::string>("host", part.substr(0, part.find(" ", 0))));
			this->originator.parts.insert(std::pair<const char *, std::string>("orig", part));
		}

		// If there is no whitespace after the user/server identifier, then the message is invalid.
		if ((tokpos = cmd.find(" ", lastpos)) == std::string::npos)
		{
			this->isValid = false;

			return;
		}

		this->command = cmd.substr(lastpos, tokpos - lastpos);
		lastpos = tokpos + 1;

		// If there is only one parameter
		if ((tokpos = cmd.find(" ", lastpos)) == std::string::npos)
		{
			if (cmd.compare(lastpos, 1, ":") == 0)
			{
				this->params.push_back(cmd.substr(lastpos+1));
			}
			else
			{
				this->params.push_back(cmd.substr(lastpos));
			}
		}
		else
		{
			// Keep adding parameters until we reach the end of the string.
			for (tokpos = cmd.find(" ", lastpos); tokpos != cmd.length()-1; tokpos = cmd.find(" ", lastpos))
			{
				if (cmd.compare(lastpos, 1, ":") == 0)
				{
					lastpos++;
					tokpos = cmd.find(" ", lastpos);
				}

				if (tokpos == std::string::npos)
				{
					this->params.push_back(cmd.substr(lastpos));
					break;
				}

				this->params.push_back(cmd.substr(lastpos, tokpos - lastpos));
				lastpos = tokpos + 1;
			}
		}
	}
};

typedef std::vector<RawCmd> Queue;
typedef std::vector<RawCmd>::iterator QueueIterator;

class CommandQueue : public MutuallyExclusive
{
protected:
	Queue queue;

public:
	CommandQueue();
	void addCommand(RawCmd cmd);
	Queue getQueue();
};

#endif